package com.ambitor.grass.sql.statement;

import java.io.StringReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.ambitor.grass.sql.data.ColumnObject;
import com.ambitor.grass.util.data.IData;

public class PreStatementImpl implements IStatement {

	private PreparedStatement statement;
	private String sql;
	private String[] names;

	public PreStatementImpl(Connection conn, String sql) throws Exception {
		this.preprocStatement(sql);
		// this.statement = conn.prepareStatement(this.sql);
		this.statement = conn.prepareStatement(this.sql,
				Statement.RETURN_GENERATED_KEYS);
	}

	public void close() throws Exception {
		this.statement.close();
	}

	public PreparedStatement getStatement() throws Exception {
		return this.statement;
	}

	public String[] getNames() {
		return this.names;
	}

	protected void setParameters(IData param) throws Exception {
		for (int i = 0; i < names.length; i++) {
			Object value = param.get(names[i]);
			if (value == null) {
				statement.setNull(i + 1, Types.VARCHAR);
			} else {
				if (value instanceof ColumnObject) {
					ColumnObject columnObject = (ColumnObject) value;
					if (columnObject.getType() == Types.LONGVARCHAR) {
						statement.setCharacterStream(i + 1, new StringReader(
								columnObject.getValue().toString()),
								columnObject.getValue().toString().length());
					}
				} else {
					statement.setObject(i + 1, value);
				}
			}
		}
	}

	private void preprocStatement(String sqlstr) throws Exception {
		class QuoteRange {
			int start;
			int length;
			String text;
		}
		Pattern patParam = Pattern.compile("(:[a-zA-Z_0-9\\$]*)");
		Pattern patQuote = Pattern.compile("('[^']*')");
		Matcher matcher;
		List quoteRanges = new ArrayList();
		matcher = patQuote.matcher(sqlstr);
		while (matcher.find()) {
			QuoteRange r = new QuoteRange();
			r.start = matcher.start();
			r.text = matcher.group();
			r.length = r.text.length();
			quoteRanges.add(r);
		}
		matcher = patParam.matcher(sqlstr);
		List keys = new ArrayList();
		while (matcher.find()) {
			String key = matcher.group().substring(1);
			if (!quoteRanges.isEmpty()) {
				boolean skip = false;
				int pos = matcher.start();
				Iterator it = quoteRanges.iterator();
				while (it.hasNext()) {
					QuoteRange r = (QuoteRange) it.next();
					if (pos >= r.start && pos < r.start + r.length) {
						skip = true;
						break;
					}
				}
				if (skip) {
					continue;
				}
			}
			if (key.length() > 0) {
				sqlstr = sqlstr.replaceFirst(":"
						+ key.replaceAll("\\Q$\\E", "\\\\\\$"), "?");
				keys.add(key);
			}
		}

		this.sql = sqlstr;
		this.names = (String[]) keys.toArray(new String[0]);
	}
}
